package com.macro.mall.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.macro.mall.common.api.CommonResult;
import com.macro.mall.dto.BucketPolicyConfigDto;
import com.macro.mall.dto.MinioUploadDto;
import com.macro.mall.service.MinioService;
import io.minio.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
public class MinioServiceImpl implements MinioService {

    @Value("${minio.endpoint}")
    private String endpoint;
    @Value("${minio.bucketName}")
    private String bucketName;
    @Value("${minio.accessKey}")
    private String accessKey;
    @Value("${minio.secretKey}")
    private String secretKey;

    @Override
    public CommonResult<MinioUploadDto> upload(MultipartFile file) {
        try {
            MinioClient minioClient = getMinioClient();
            checkAndCreateBucket(minioClient);

            String filename = file.getOriginalFilename();
            String objectName = generateObjectName(filename);

            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .contentType(file.getContentType())
                    .stream(file.getInputStream(), file.getSize(), ObjectWriteArgs.MIN_MULTIPART_SIZE)
                    .build();
            minioClient.putObject(putObjectArgs);

            log.info("文件上传成功!");
            return CommonResult.success(createUploadDto(filename, objectName));
        } catch (Exception e) {
            log.error("上传发生错误: {}", e.getMessage(), e);
            return CommonResult.failed("文件上传失败");
        }
    }

    @Override
    public CommonResult<MinioUploadDto> uploadQRCode(String number) {
        try {
            // 生成二维码图片
            BufferedImage qrCodeImage = generateQRCodeImage(number, 300, 300);

            // 转换为字节数组
            byte[] imageBytes = convertImageToBytes(qrCodeImage, "png");

            // 获取MinIO客户端并检查存储桶
            MinioClient minioClient = getMinioClient();
            checkAndCreateBucket(minioClient);

            // 生成文件名和对象名
            String filename = "qrcode_" + number + ".png";
            String objectName = generateObjectName(filename);

            // 上传到MinIO
            uploadToMinio(minioClient, imageBytes, objectName, "image/png");

            log.info("二维码上传成功!");
            return CommonResult.success(createUploadDto(filename, objectName));
        } catch (Exception e) {
            log.error("生成或上传二维码发生错误: {}", e.getMessage(), e);
            return CommonResult.failed("二维码生成失败");
        }
    }

    @Override
    public CommonResult<Void> delete(String objectName) {
        try {
            MinioClient minioClient = getMinioClient();
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build());
            return CommonResult.success(null);
        } catch (Exception e) {
            log.error("删除文件发生错误: {}", e.getMessage(), e);
            return CommonResult.failed("文件删除失败");
        }
    }

    // ========== 私有方法 ==========

    private MinioClient getMinioClient() {
        return MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
    }

    private void checkAndCreateBucket(MinioClient minioClient) throws Exception {
        boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (!isExist) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            setBucketPolicy(minioClient);
        }
    }

    private void setBucketPolicy(MinioClient minioClient) throws Exception {
        BucketPolicyConfigDto bucketPolicyConfigDto = createBucketPolicyConfigDto();
        minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
                .bucket(bucketName)
                .config(JSONUtil.toJsonStr(bucketPolicyConfigDto))
                .build());
    }

    private BucketPolicyConfigDto createBucketPolicyConfigDto() {
        BucketPolicyConfigDto.Statement statement = BucketPolicyConfigDto.Statement.builder()
                .Effect("Allow")
                .Principal("*")
                .Action("s3:GetObject")
                .Resource("arn:aws:s3:::" + bucketName + "/*.**").build();
        return BucketPolicyConfigDto.builder()
                .Version("2012-10-17")
                .Statement(CollUtil.toList(statement))
                .build();
    }

    private String generateObjectName(String filename) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        return sdf.format(new Date()) + "/" + filename;
    }

    private MinioUploadDto createUploadDto(String filename, String objectName) {
        MinioUploadDto dto = new MinioUploadDto();
        dto.setName(filename);
        dto.setUrl(endpoint + "/" + bucketName + "/" + objectName);
        return dto;
    }

    private BufferedImage generateQRCodeImage(String content, int width, int height) throws Exception {
        Map<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        hints.put(EncodeHintType.MARGIN, 1);

        BitMatrix bitMatrix = new MultiFormatWriter().encode(
                content,
                BarcodeFormat.QR_CODE,
                width,
                height,
                hints
        );

        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
            }
        }
        return image;
    }

    private byte[] convertImageToBytes(BufferedImage image, String format) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(image, format, baos);
        return baos.toByteArray();
    }

    private void uploadToMinio(MinioClient minioClient, byte[] bytes, String objectName, String contentType) throws Exception {
        PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .contentType(contentType)
                .stream(new ByteArrayInputStream(bytes), bytes.length, ObjectWriteArgs.MIN_MULTIPART_SIZE)
                .build();
        minioClient.putObject(putObjectArgs);
    }
}
